using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using EnvDTE80;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using EnvDTE;
using Microsoft.VisualStudio.TeamFoundation;

namespace TeamReview.Services
{
  public static class TFSService
  {
    private static TeamFoundationServer tfs;
    private static readonly List<string> users = new List<string>();
    private static readonly object userLock = new Object();
    private static TeamProject teamProject;
    private static WorkItemStore workItemStore;
    private static FieldDefinitionCollection fields;
    private static AllowedValuesCollection priorityAllowedValues;
    private static DTE2 shellDTE;
    private static SolutionEvents solutionEvents;
    public static event TeamProjectChangeEventHandler TeamProjectChanged;
    private static VersionControlServer versionControlServer;
    private static TeamFoundationServerExt tfsExt;
    private static VersionControlExt versionControl;

    private static void OnTeamProjectChanged()
    {

        if (teamProject == null)
            Instrumentation.Trace("TeamReview is not available because the current solution is not part of a Team Project.");
        else
            Instrumentation.Trace(String.Format(@"Team Project changed to {0}\{1}",
                                      teamProject.TeamFoundationServer.Name, teamProject.Name));

        if (TeamProjectChanged == null)
        return;

      TeamProjectChanged();
    }


    public static VersionControlServer VersionControlServer
    {
      get { return VersionControlServer; }
    }

    public static TeamProject TeamProject
    {
      get { return teamProject; }
    }

    public static VersionControlExt VersionControlExt
    {
      get { return versionControl; }
    }

    public static TeamFoundationServerExt TeamFoundationServerExt
    {
      get { return tfsExt; }
    }

    public static TeamFoundationServer TeamFoundationServer
    {
      get { return tfs; }
    }

    public static WorkItemStore WorkItemStore
    {
      get { return workItemStore; }
    }

    public static StoredQueryCollection StoredQueries
    {
      get
      {
        return workItemStore.Projects[teamProject.Name].StoredQueries;
      }
    }

    public static string CurrentUserDisplayName
    {
      get
      {
        if (workItemStore.UserDisplayMode == UserDisplayMode.AccountName)
          return workItemStore.TeamFoundationServer.AuthenticatedUserName;

        return workItemStore.TeamFoundationServer.AuthenticatedUserDisplayName;
      }
    }

    public static bool IsTFSProject
    {
      get
      {
        return !(teamProject == null);
      }
    }

    public static void Configure(DTE2 dte)
    {
      shellDTE = dte;
      solutionEvents = shellDTE.Events.SolutionEvents;
      solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
      SetTeamProject();
    }

    public static void Shutdown()
    {
      try
      {
        solutionEvents.Opened -= new _dispSolutionEvents_OpenedEventHandler(SetTeamProject);
      }
      catch { }
    }


    public static bool HasField(string fieldName)
    {
      return fields.Contains(fieldName);
    }


    static void SolutionEvents_Opened()
    {
      SetTeamProject();
    }



    public static AllowedValuesCollection PriorityAllowedValues
    {
      get { return priorityAllowedValues; }
    }

    public static void SetTeamProject()
    {

      TeamProject newProject;

      if (shellDTE.Solution == null)
        newProject = null;
      else
      {
          string solution = "not open";
          if (!String.IsNullOrEmpty(shellDTE.Solution.FullName))
              solution = shellDTE.Solution.FullName;

          Instrumentation.Trace(String.Format(@"Visual Studio Solution is {0}", solution));


        versionControl = shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt") as VersionControlExt;
        if (versionControl == null || versionControl.SolutionWorkspace == null)
          newProject = null;
        else
          newProject = versionControl.SolutionWorkspace.GetTeamProjectForLocalPath(shellDTE.Solution.FullName);
      }

      if (teamProject != newProject)
      {
        teamProject = newProject;

        if (teamProject == null)
        {
            OnTeamProjectChanged();
            return;
        }
       

        tfs = teamProject.TeamFoundationServer;
        tfsExt = (TeamFoundationServerExt)shellDTE.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt");
        versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

        workItemStore = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
        if (workItemStore == null)
          throw new NullReferenceException("Did not retrieve WorkItemStore from Team Foundation Server");
        fields = workItemStore.Projects[teamProject.Name].WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE].FieldDefinitions;

        if (fields.Contains(Constants.CODE_PRIORITY_FIELD))
        {
          priorityAllowedValues = fields[Constants.CODE_PRIORITY_FIELD].AllowedValues;

        }
        else
        {
          priorityAllowedValues = null;
        }

        System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(SetUsers));
        thread.Start();
        OnTeamProjectChanged();
      }
    }

    public static TeamProject GetTeamProject()
    {
      return teamProject;
    }

    private static void SetUsers()
    {
      lock (userLock)
      {
        if (tfs == null)
          return;

        users.Clear();

        IGroupSecurityService gss = (IGroupSecurityService)tfs.GetService(typeof(IGroupSecurityService));

        Identity[] projectGroups = gss.ListApplicationGroups(teamProject.ArtifactUri.ToString());
        Identity serviceGroup = gss.ReadIdentity(SearchFactor.ServiceApplicationGroup, null, QueryMembership.None);
        Identity[] serviceGroups = new Identity[1] { serviceGroup };

        AddCodeChangers(gss, projectGroups);
        AddCodeChangers(gss, serviceGroups);

        users.Sort();
      }
    }

    private static void AddCodeChangers(IGroupSecurityService gss, Identity[] projectGroups)
    {
      foreach (Identity projectGroup in projectGroups)
      {
        if (!(projectGroup.DisplayName.Equals("Contributors") ||
              projectGroup.DisplayName.Equals("Project Administrators") ||
              projectGroup.DisplayName.Equals("Service Accounts")))
          continue;

        Identity directMembers = gss.ReadIdentity(SearchFactor.Sid, projectGroup.Sid, QueryMembership.Expanded);
        foreach (string memberSid in directMembers.Members)
        {
          Identity member = gss.ReadIdentity(SearchFactor.Sid, memberSid, QueryMembership.None);

          if (!member.SecurityGroup && member.Type != IdentityType.WindowsGroup
              && member.Type != IdentityType.ApplicationGroup)
          {
            if (!users.Contains(member.DisplayName))
              users.Add(member.DisplayName);
          }
        }
      }
    }

    public static List<string> GetTeamUsers()
    {
      lock (userLock)
      {
        return users;
      }
    }

    public static WorkItemTypeCollection GetAvailableTeamWorkItemTypes
    {
      get { return workItemStore.Projects[teamProject.Name].WorkItemTypes; }
    }
  }
}
